package com.ccl.keyassistant.utils;

/*
 *项目名     keyassistant
 *包名       com.ccl.keyassistant.utils
 *文件名     BluetoothService
 *创建者     ccl
 *创建时间   2019-03-14 12:48
 *注解       蓝牙连接的相关类，参考Google示例代码完成
 */


import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
 * 这个类完成了蓝牙连接的基本设置与管理，包含了作为服务端的蓝牙监听，作为客户端的蓝牙
 * 连接以及连接成功后的数据传输线程。
 */
public class BluetoothService {

    // Name for the SDP record when creating server socket
    private static final String NAME_SDP = "keyassistant";

    // Unique UUID for this application
    private static final UUID MY_UUID =
            UUID.fromString("39b6a2c7-ffd8-49ab-a89f-ad0465ca6cac");

    private final BluetoothAdapter mAdapter;
    private AcceptThread mAcceptThread;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;

    private int mState;


    // 表明蓝牙当前的状态
    public static final int STATE_NONE = 0;       // 空状态
    public static final int STATE_LISTEN = 1;     // 作为服务端监听
    public static final int STATE_CONNECTING = 2; // 正在向外连接状态
    public static final int STATE_CONNECTED = 3;  // 已连接一个远程设备

    /**
     * Constructor. Prepares a new BluetoothChat session.
     *
     * @param context The UI Activity Context
     */
    public BluetoothService(Context context) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mState = STATE_NONE;
    }

//    private synchronized void updateState() {
//        mState = getState();
//    }

    /**
     * 开启ConnectThread 去开始连接一个远程设备
     *
     * @param device 需要连接的远程设备
     */
    public synchronized void connect(BluetoothDevice device) {
        L.d("connect to: " + device);

        // 取消所有正在尝试的连接
        if (mState == STATE_CONNECTING) {
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
        }

        // 取消所有已成功的连接
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        // 通过获取到的设备开始连接
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
//        updateState();
    }

    /**
     * 作为客户端发起连接时的方法
     */
    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            mmDevice = device;
            BluetoothSocket tmp = null;

            // 通过已获得的BluetoothDevice得到一个BluetoothSocket连接
            try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                L.e("连接失败：" + device.getAddress(), e);
            }
            mmSocket = tmp;
            mState = STATE_CONNECTING;
            L.i("获取BluetoothSocket  " + mmSocket);
        }

        @Override
        public void run() {
            L.i("获取连接套接字成功，开启读写线程 ");

            // 取消扫描以加快连接
            mAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // 连接成功或者返回异常
                mmSocket.connect();
                L.i("mmSocket  " + mmSocket);
            } catch (IOException e) {
                // Close the socket
                try {
                    mmSocket.close();
                } catch (IOException e2) {
                    L.e("关闭套接字失败", e2);
                }
                connectionFailed();
                return;
            }

            // Reset the ConnectThread because we're done
            synchronized (BluetoothService.this) {
                mConnectThread = null;
            }
//            manageConnectedSocket(mmSocket);
            // Start the connected thread
            connected(mmSocket, mmDevice);
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                L.e("关闭连接失败", e);
            }
        }
    }


    /**
     * 开始ConnectedThread 去管理连接的蓝牙设备
     *
     * @param socket 已连接设备获取到的BluetoothSocket
     * @param device 已连接的蓝牙设备
     */
    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
        L.d("开始数据传输");

        // 取消所有正在尝试的连接
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        // 取消所有已成功的连接
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        //关闭连接线程使得只有一个连接
        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }

        // 开启线程管理连接并执行数据传输
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();

        // Send the name of the connected device back to the UI Activity
        // Update UI title
//        updateState();
    }

    /**
     * 关闭所有线程
     */
    public synchronized void stop() {
        L.d("stop");

        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }
        mState = STATE_NONE;
        // Update UI title
//        updateState();
    }

    /**
     * 向已连接的线程中写数据
     *
     * @param out The bytes to write
     */
    public void write(byte[] out) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) {
                L.i("write state:  " +mState);
                return;
            }
            r = mConnectedThread;
        }
        r.write(out);
    }

    /**
     * 当设备连接时用于处理发送和接收的消息
     */
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            L.d("数据传输线程......");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                L.e(" 获取输入输出流失败", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
            mState = STATE_CONNECTED;
        }

        @Override
        public void run() {
//            L.i( "ConnectedThread  run 管理连接线程");
//            byte[] buffer = new byte[1024];
//            int bytes;
//
//            // Keep listening to the InputStream while connected
//            while (mState == STATE_CONNECTED) {
//                try {
////                    // 从输入流读取数据
//////                    bytes = mmInStream.read(buffer);
////                    String str = new String(buffer);
//////                    // TODO 传递数据出去
//////                    L.i("从输入流读取数据"+"  count:"+bytes+"   "+str);/
//                    mmOutStream.write("ababs".getBytes());
////
//                } catch (IOException e) {
//                    L.e("ConnectedThread  run 未连接", e);
//                    connectionLost();
//                    break;
//                }
//            }
            L.i("输入流：" + mmInStream);
            L.i("输出流：" + mmOutStream);
        }

        /**
         * 向已连接设备写数据
         *
         * @param buffer The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                L.i("write 输入流：" + mmInStream);
                L.i("write 输出流：" + mmOutStream);
                mmOutStream.write(buffer);

                mmOutStream.flush();
//                mmOutStream.close();
                // Share the sent message back to the UI Activity
                L.i("向已连接设备写数据");
            } catch (IOException e) {
                L.e("写数据时异常", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                L.e("关闭套接字连接失败", e);
            }
        }
    }


    /**
     * Return the current connection state.
     */
    public synchronized int getState() {
        return mState;
    }

    /**
     * 作为服务端开启AcceptThread 去监听连接
     */
    public synchronized void start() {
        L.d("start");

        // 取消所有正在尝试的连接
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        // 取消所有已成功的连接
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        // 开启连接监听
        if (mAcceptThread == null) {
            mAcceptThread = new AcceptThread();
            mAcceptThread.start();
        }
//        updateState();
    }


    /**
     * 连接失败
     */
    private void connectionFailed() {
        mState = STATE_NONE;
//        updateState();
        // 重启监听模块
        BluetoothService.this.start();
    }

    /**
     * 连接丢失
     */
    private void connectionLost() {
        mState = STATE_NONE;
//        updateState();
        // Start the service over to restart listening mode
        BluetoothService.this.start();
    }

    /**
     * 作为服务端监听连接，直到连接成功获取取消
     */
    private class AcceptThread extends Thread {
        // The local server socket
        private final BluetoothServerSocket mmServerSocket;
        private String mSocketType;

        public AcceptThread() {
            BluetoothServerSocket tmp = null;

            // 创建一个新的监听端口
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SDP,
                        MY_UUID);
            } catch (IOException e) {
                L.e("listen() failed", e);
            }
            mmServerSocket = tmp;
            mState = STATE_LISTEN;
        }

        @Override
        public void run() {
            L.d("开始监听线程 AcceptThread" + this);
            setName("AcceptThread" + mSocketType);

            BluetoothSocket socket = null;

            // 如果未连接则监听端口
            while (mState != STATE_CONNECTED) {
                try {
                    // 这是一个线程阻塞方法，只有连接成功或者发生异常时才会返回
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    L.e("AcceptThread accept() failed", e);
                    break;
                }

                // 如果已经连接
                if (socket != null) {
                    synchronized (BluetoothService.this) {
                        switch (mState) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                // 连接设备.
                                connected(socket, socket.getRemoteDevice()
                                );
                                break;
                            case STATE_NONE:
                            case STATE_CONNECTED:
                                // 开启新的连接.
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                    L.e("AcceptThread 无法关闭不需要的连接", e);
                                }
                                break;
                        }
                    }
                }
            }
            L.i("AcceptThread 监听线程结束");

        }

        public void cancel() {
            L.d(" AcceptThread 监听线程cancel " + this);
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                L.e(" AcceptThread Socket Type" + mSocketType + "close() of server failed", e);
            }
        }
    }


}
